/**
 @file sys_tmm_l2_fdb.c


 @author  Copyright (C) 2022 Centec Networks Inc.  All rights reserved.

 @date 2022-12-17

 @version v1.0

*/

/****************************************************************************
 *
* Header Files
*
****************************************************************************/
#include "ctc_l2.h"
#include "ctc_pdu.h"
#include "ctc_error.h"
#include "sys_usw_l2_fdb.h"
#include "drv_api.h"
#include "sys_usw_register.h"

int32
sys_tmm_l2_decode_dsmac(uint8 lchip, void* p_sys, uint32 ad_index, void* p_ds_mac)
{
    uint32 destmap = 0;
    uint32 temp_flag = 0;
    uint32 temp_gport = 0;
    ds_t ds_mac;
    uint32  cmd = 0;
    uint16  ecmp_group_id = 0;
    uint8 is_dft_entry = IS_DEFAULT_ENTRY_INDEX(ad_index);
    sys_dsmac_decode_tmm_t* dsmac =NULL;
    sys_l2_info_t* psys =(sys_l2_info_t*)p_sys;

    CTC_PTR_VALID_CHECK(psys);

    SYS_FDB_DBG_OUT(CTC_DEBUG_LEVEL_FUNC, "%s()\n", __FUNCTION__);
    SYS_FDB_DBG_OUT(CTC_DEBUG_LEVEL_INFO, "Read dsmac, ad_index:0x%x\n", ad_index);

    if (p_ds_mac == NULL)
    {
        cmd = DRV_IOR(DsMac_t, DRV_ENTRY_FLAG);
        CTC_ERROR_RETURN(DRV_IOCTL(lchip, ad_index, cmd, &ds_mac));
        dsmac = (sys_dsmac_decode_tmm_t*)&ds_mac;
    }
    else
    {
        dsmac = (sys_dsmac_decode_tmm_t*)p_ds_mac;
    }

    psys->ad_index        = ad_index;
    SYS_L2_OP_FLAG(temp_flag, CTC_L2_FLAG_IS_STATIC, dsmac->isStatic);
    SYS_L2_OP_FLAG(temp_flag, CTC_L2_FLAG_BIND_PORT, dsmac->srcMismatchDiscard);
    SYS_L2_OP_FLAG(temp_flag, CTC_L2_FLAG_PROTOCOL_ENTRY, dsmac->protocolExceptionEn);
    SYS_L2_OP_FLAG(temp_flag, CTC_L2_FLAG_COPY_TO_CPU, (((dsmac->u3__2_20>>15)&0x1) && !dsmac->u3Type));
    SYS_L2_OP_FLAG(temp_flag, CTC_L2_FLAG_SRC_DISCARD, dsmac->srcDiscard);
    SYS_L2_OP_FLAG(temp_flag, CTC_L2_FLAG_SELF_ADDRESS, dsmac->localAddress);
    SYS_L2_OP_FLAG(temp_flag, CTC_L2_FLAG_REMOTE_DYNAMIC, dsmac->userDefine);
    ecmp_group_id = (dsmac->u1Type == 2) ? ((dsmac->u1__0_30>>1)&0xFFF) : 0 ;
    

    /* | default| tid| gport| vport|*/
    destmap = is_dft_entry? GetDsMacDefault(V, u_g1_dsFwd_destMap_f, dsmac) : ((dsmac->u1__0_30 >> 18) | ((dsmac->u1__31_44&0x7F) << 13));
        /*decode addr type*/
    psys->addr_type = SYS_L2_ADDR_TYPE_UC;
    psys->is_logic_port = dsmac->learnSource;
    if (ad_index >= sys_usw_l2_get_dsmac_base(lchip, SYS_L2_DSMAC_BASE_TRUNK))
    {
        psys->rsv_ad_index = 1;
        psys->destmap = destmap;
        if (psys->is_logic_port)
        {
            temp_gport = dsmac->u2_;
            psys->addr.l2_addr.is_logic_port = 1;
        }
        else
        {
            temp_gport = SYS_MAP_DRV_GPORT_TO_CTC_GPORT(dsmac->u2_);
        }
        psys->addr.l2_addr.gport = temp_gport;
        goto end;
    }
    else if (ad_index == pl2_usw_master[lchip]->ad_index_drop)
    {
        CTC_SET_FLAG(temp_flag, CTC_L2_FLAG_DISCARD);
    }
    else if (!ecmp_group_id &&  !dsmac->u1Type && (SYS_FDB_DISCARD_FWD_PTR == ((dsmac->u1__0_30>>1)&0xFFFF)))
    {
        CTC_SET_FLAG(temp_flag, CTC_L2_FLAG_DISCARD);
    }

    if(dsmac->srcDiscard &&  dsmac->macSaExceptionEn)
    {
        CTC_SET_FLAG(temp_flag, CTC_L2_FLAG_SRC_DISCARD_TOCPU);
    }

    if (((CTC_FLAG_ISSET(temp_flag, CTC_L2_FLAG_IS_STATIC) && /*mcast is static.*/
        (dsmac->u1Type==1 || CTC_FLAG_ISSET(temp_flag, CTC_L2_FLAG_DISCARD))) 
        || GetDsMacDefault(V, isStatic_f, dsmac))&& IS_MCAST_DESTMAP(destmap))
    {
        if (IS_DEFAULT_ENTRY_INDEX(ad_index))
        {
            psys->addr_type = SYS_L2_ADDR_TYPE_DEFAULT;
            psys->addr.l2df_addr.l2mc_grp_id = (destmap &0xFFFF);
            if (GetDsMacDefault(V, categoryIdValid_f, dsmac))
            {
                psys->addr.l2df_addr.cid = GetDsMacDefault(V, categoryId_f, dsmac);
            }
        }
        else if(!psys->is_logic_port)
        {
            psys->addr_type = SYS_L2_ADDR_TYPE_MC;
            psys->addr.l2mc_addr.l2mc_grp_id = (destmap &0xFFFF);
            if ((dsmac->u3__2_20>>14&0x1) && !dsmac->u3Type)
            {
                psys->addr.l2mc_addr.cid =  dsmac->u3__0_1 | ((dsmac->u3__2_20&0x3FFF)<<2);
            }
            else if (!(dsmac->u3__2_20>>14&0x1) && !dsmac->u3Type)
            {
                psys->addr.l2mc_addr.service_id = dsmac->u3__0_1 | ((dsmac->u3__2_20&0x3FFF)<<2);
            }
        }
    }
    psys->merge_dsfwd = (dsmac->u1Type == 1) ? 1 : 0;


    if(psys->merge_dsfwd)
    {
        psys->aps_valid = (dsmac->u1__31_44>>6)&0x1;
        destmap = (dsmac->u1__0_30>>18) | ((dsmac->u1__31_44&0x3F)<<13);
        psys->dsnh_offset = dsmac->u1__0_30&0x3FFFF;
        psys->ecmp_valid = SYS_IS_ECMP_DESTMAP(destmap);
        if(psys->ecmp_valid)
        {
            psys->destmap = SYS_DECODE_ECMP_DESTMAP(destmap);
        }
        else
        {
            psys->destmap = destmap;
        }
    }
    else
    {
        psys->ecmp_valid = ecmp_group_id != 0;
        psys->fwd_ptr    = psys->ecmp_valid ? ((dsmac->u1__0_30>>13)&0xFFFF) : ((dsmac->u1__0_30>>1)&0xFFFF);

        if (psys->ecmp_valid)
        {
            psys->destmap = ecmp_group_id ;
        }
    }

    if(psys->addr_type == SYS_L2_ADDR_TYPE_UC)
    {
        psys->addr.l2_addr.flag = temp_flag;

        if (psys->is_logic_port)
        {
            temp_gport = dsmac->u2_;
            psys->addr.l2_addr.is_logic_port = 1;
        }
        else
        {
            temp_gport = SYS_MAP_DRV_GPORT_TO_CTC_GPORT(dsmac->u2_);
        }
        psys->addr.l2_addr.gport = temp_gport;
        if (dsmac->u3__2_20>>14&0x1)
        {
            psys->addr.l2_addr.cid = dsmac->u3__0_1 | ((dsmac->u3__2_20&0x3FFF)<<2);
        }
    }
    else if(psys->addr_type == SYS_L2_ADDR_TYPE_MC)
    {
        psys->addr.l2mc_addr.flag = temp_flag;
    }

    psys->share_grp_en = !dsmac->fastLearningEn;
end:
    return CTC_E_NONE;
}

int32
sys_tmm_l2_encode_dsmac(uint8 lchip, void* p_dsmac, void* p_sys)
{
    sys_dsmac_decode_tmm_t* dsmac = (sys_dsmac_decode_tmm_t*)p_dsmac;
    sys_l2_info_t* psys = (sys_l2_info_t*)p_sys;
    uint16  cid = 0;
    uint32   temp_flag = 0;
    uint16 service_id = 0;
  
    if (psys->addr_type == SYS_L2_ADDR_TYPE_UC)
    {
        cid = CTC_FLAG_ISSET(psys->addr.l2_addr.flag, CTC_L2_FLAG_PON_EN)? 0 : psys->addr.l2_addr.cid;
        service_id = psys->addr.l2_addr.service_id;
        temp_flag = psys->addr.l2_addr.flag;
        dsmac->sourcePortCheckEn = 1;

        if (!CTC_FLAG_ISSET(temp_flag, CTC_L2_FLAG_IS_STATIC) && !CTC_FLAG_ISSET(psys->addr.l2_addr.flag, CTC_L2_FLAG_DISCARD)
            && !pl2_usw_master[lchip]->station_move_no_learn)
        {
            dsmac->srcMismatchLearnEn = 1;
        }

        if (CTC_FLAG_ISSET(temp_flag, CTC_L2_FLAG_BIND_PORT))
        {
            dsmac->srcMismatchDiscard = 1;
        }

        if (psys->is_logic_port)
        {
            SYS_FDB_DBG_OUT(CTC_DEBUG_LEVEL_INFO, " DsMac.logic_port 0x%x\n", psys->addr.l2_addr.gport);
            dsmac->learnSource = 1;
            dsmac->u2_ = psys->addr.l2_addr.gport;
            dsmac->logicDestPortValid = 1;

            if (psys->addr.l2_addr.policer_id &&
                psys->xgpon_en && CTC_FLAG_ISSET(psys->addr.l2_addr.flag, CTC_L2_FLAG_PON_EN))
            {
                uint16 policer_ptr = 0;
                uint8 cos_index = 0;
                sys_qos_policer_param_t policer_param;

                sal_memset(&policer_param, 0, sizeof(sys_qos_policer_param_t));
                policer_param.glb_policer = 1;
                policer_param.policer_id = psys->addr.l2_addr.policer_id;
                policer_param.sys_policer_type = SYS_QOS_POLICER_TYPE_FLOW;
                CTC_ERROR_RETURN(sys_usw_qos_policer_index_get(lchip, &policer_param));
                cos_index = psys->addr.l2_addr.cid; /* cid used for cos_index when Pon_en */
                if ((cos_index && !policer_param.is_bwp) || (cos_index > (MCHIP_CAP(SYS_CAP_QOS_POLICER_MAX_COS_LEVEL) - 1)))
                {
                    return CTC_E_INVALID_PARAM;
                }
                policer_ptr = policer_param.policer_ptr + cos_index;
                 
                dsmac->u3Type = 1;/* use u3.g2 */
                dsmac->u3__2_20 |= policer_ptr;
                dsmac->u3__0_1 |= (policer_param.level << 1);
            }
        }
        else
        {
            SYS_FDB_DBG_OUT(CTC_DEBUG_LEVEL_INFO, " DsMac.gport 0x%x\n", psys->addr.l2_addr.gport);
            dsmac->learnSource = 0;
            dsmac->u2_ = SYS_MAP_CTC_GPORT_TO_DRV_GPORT(psys->addr.l2_addr.gport);
        }
        dsmac->isConversation = 0;

        if (!psys->merge_dsfwd)
        {
            if (psys->ecmp_valid)
            {
                {
                    dsmac->u1Type = 2;
                    dsmac->u1__0_30 |= ((psys->destmap << 1)|(psys->fwd_ptr<<13));
                }
            }
            else
            {
                dsmac->u1Type = 0;
                dsmac->u1__0_30 |= (psys->fwd_ptr<<1);
            }
        }
        else
        {
            dsmac->u1Type = 1;
            dsmac->u1__31_44 |= ((psys->nh_ext << 8) | (psys->destmap >> 13) | (psys->by_pass_ingress_edit << 7) | (psys->aps_valid << 6) | (psys->tunnel_ecmp_high << 9));
            dsmac->u1__0_30 |= (((psys->destmap&0x1FFF) << 18) | psys->dsnh_offset);
        }

        /* dsmac for router mac match */
        if (CTC_FLAG_ISSET(temp_flag, CTC_L2_FLAG_RMAC_MATCH))
        {
            dsmac->isRouterMac = 1;
        }

    }
    else  /* (psys->flag.type_l2mc)*/
    {
        cid = psys->addr.l2mc_addr.cid;
        service_id = psys->addr.l2mc_addr.service_id;
        temp_flag = psys->addr.l2mc_addr.flag;
        /* always use nexthop */
        dsmac->isConversation = 0;
        /* TsingMa.MX */
        dsmac->u1Type = 1;
        dsmac->u1__31_44 |= (SYS_ENCODE_MCAST_IPE_DESTMAP(psys->addr.l2mc_addr.l2mc_grp_id) >> 13);
        dsmac->u1__0_30 |= (((SYS_ENCODE_MCAST_IPE_DESTMAP(psys->addr.l2mc_addr.l2mc_grp_id)&0x1FFF) << 18));
    }

    if ((CTC_FLAG_ISSET(temp_flag, CTC_L2_FLAG_IS_STATIC)) || (psys->addr_type == SYS_L2_ADDR_TYPE_DEFAULT && !psys->revert_default))
    {
        dsmac->isStatic = 1;
    }

    if (CTC_FLAG_ISSET(temp_flag, CTC_L2_FLAG_SELF_ADDRESS))
    {
        dsmac->localAddress = 1;
    }
    if (CTC_FLAG_ISSET(temp_flag, CTC_L2_FLAG_DISCARD))
    {
        dsmac->u1Type = 0;
        dsmac->u1__0_30 &= (~(0xFFFF<<1));
        dsmac->u1__31_44 &= (~(0x3F<<2));
    }
    if (CTC_FLAG_ISSET(temp_flag, CTC_L2_FLAG_SRC_DISCARD))
    {
        dsmac->srcDiscard = 1;
        dsmac->macSaExceptionEn = 0;
    }

    if (CTC_FLAG_ISSET(temp_flag, CTC_L2_FLAG_SRC_DISCARD_TOCPU))
    {
        dsmac->srcDiscard = 1;
        dsmac->macSaExceptionEn = 1;
    }

    if (CTC_FLAG_ISSET(temp_flag, CTC_L2_FLAG_COPY_TO_CPU))
    {
        dsmac->u3Type = 0;
        dsmac->u3__2_20 |= ((1 << 15) | (CTC_L2PDU_ACTION_INDEX_MACDA << 16));
    }

    if (CTC_FLAG_ISSET(temp_flag, CTC_L2_FLAG_PROTOCOL_ENTRY))
    {
        dsmac->protocolExceptionEn = 1;
    }

    if (CTC_FLAG_ISSET(temp_flag, CTC_L2_FLAG_REMOTE_DYNAMIC))
    {
        dsmac->userDefine = 1;
    }
    dsmac->stormCtlEn = 1;
    dsmac->fastLearningEn = !psys->share_grp_en;
    /*dsmac for cid*/
    if (cid)
    {
        dsmac->u3Type = 0;
        dsmac->u3__2_20 |= ((1 << 14) | (cid >> 2));
        dsmac->u3__0_1 = cid&0x3;
    }

    if (service_id)
    {
        dsmac->u3Type = 0;
        dsmac->u3__2_20 |= ((0 << 14) | (service_id >> 2));
        dsmac->u3__0_1 = service_id&0x3;
    }
    return CTC_E_NONE;

}


int32
sys_tmm_l2_encode_key(uint8 lchip, void* p_ds_key, uint32* hw_mac, uint32 fid, uint32 ad_index)
{
    sys_fdbkey_encode_tmm_t* key = (sys_fdbkey_encode_tmm_t*)p_ds_key;
    
    key->valid =1;
    key->dsAdIndex = ad_index;
    key->vsiId_0_8 = fid&0x1FF;
    key->vsiId_9_14 = fid>>9;
    key->hashType = DRV_ENUM(DRV_FIBHOST0PRIMARYHASHTYPE_MAC);
    key->mappedMac_0_25 = hw_mac[0]&0x3FFFFFF;
    key->mappedMac_26_47 = (hw_mac[0]>>26) | (hw_mac[1]&0xFFFF)<<6;
    return CTC_E_NONE;
}

